package practise.tree;


import javax.transaction.NotSupportedException;
import java.util.ArrayDeque;
import java.util.Iterator;
import java.util.Queue;
import java.util.Stack;

public class IteratableTree implements Iterable<TreeNode> {

    private TreeNode treeNode ;
    private int type;
    private int level;

    public IteratableTree(TreeNode treeNode, int type) {
        this.type=type;
        this.treeNode = treeNode;
    }

    public IteratableTree(TreeNode treeNode, int type,int level) {
        this.type=type;
        this.treeNode = treeNode;
        this.level = level;
    }

    @Override
    public Iterator<TreeNode> iterator() {
        switch (type){
            case 1: return new MidTreeIterator(treeNode);
            case 2: return new WidthTreeIterator(treeNode);
            case 3 : return  new DepthTreeIterator(treeNode);
        }
        return null;
    }
}

class LevelNodeIterator implements Iterator<TreeNode>{
    private TreeNode treeNode;
    private Queue<TreeNode> queue;
    private int level;
    private int leafCount=0;

    LevelNodeIterator(TreeNode treeNode , int level) {
        this.treeNode = treeNode;
        queue = new ArrayDeque<TreeNode>();

        if(treeNode!=null){
            queue.offer(treeNode);
            getPreLevelNodes(treeNode,level);
        }

    }

    private void getPreLevelNodes(TreeNode node ,int level){
        int currentLevel = 1;
        while(currentLevel<level){
            currentLevel++;
            for(TreeNode treeNode1: queue){
                if(treeNode1.getLeft()!=null){
                    queue.offer(treeNode1.getLeft());
                }
                if(treeNode1.getRight()!=null){
                    queue.offer(treeNode1.getRight());
                }
            }
        }
    }

    @Override
    public boolean hasNext() {
        return !queue.isEmpty();
    }

    @Override
    public TreeNode next() {
        TreeNode treeNode1 = null ;
        while(true){
            if(leafCount==0){
                treeNode1 = queue.peek();
                if(treeNode1.getLeft()!=null){
                    treeNode1 = treeNode1.getLeft();
                    if(treeNode1.getRight()!=null){
                        queue.offer(treeNode1.getRight());
                        leafCount++;
                    }
                }
                else if(treeNode1.getRight()!=null){
                    return treeNode1.getRight();
                }
            }
            else{
                leafCount--;
                return queue.peek();
            }
        }

    }

    @Override
    public void remove() {

    }
}

class MidTreeIterator implements Iterator<TreeNode>{
    private TreeNode treeNode;
    private Stack<TreeNode> stack;

    MidTreeIterator(TreeNode treeNode) {
        this.treeNode = treeNode;
        stack = new Stack<TreeNode>();
        if(treeNode!=null)
            pushAllLeft(treeNode);
    }

    private void pushAllLeft(TreeNode node){
        while(node!=null && node.getLeft()!=null){
            stack.push(node.getLeft());
        }

    }

    @Override
    public boolean hasNext() {
        return !stack.isEmpty();
    }

    @Override
    public TreeNode next() {
       TreeNode treeNode1 = stack.pop();
        pushAllLeft(treeNode1.getRight());
        return treeNode1;
    }

    @Override
    public void remove() {

    }
}

class WidthTreeIterator implements Iterator<TreeNode>{
    private TreeNode treeNode;
    private Queue<TreeNode> queue;

    WidthTreeIterator(TreeNode treeNode) {
        this.treeNode = treeNode;
        queue = new ArrayDeque<TreeNode>();
        if(treeNode!=null){
            queue.offer(treeNode);
        }
    }

    @Override
    public boolean hasNext() {
        return !queue.isEmpty();
    }

    @Override
    public TreeNode next() {
        TreeNode treeNode1 = queue.poll();
        if(treeNode1.getLeft()!=null){
            queue.offer(treeNode1.getLeft());
        }
        if(treeNode1.getRight()!=null){
            queue.offer(treeNode1.getRight());
        }
        return treeNode1;
    }

    @Override
    public void remove() {

    }
}

class DepthTreeIterator implements Iterator<TreeNode>{
    private TreeNode treeNode;
    private Stack<TreeNode> stack;

    DepthTreeIterator(TreeNode treeNode) {
        this.treeNode = treeNode;
        stack = new Stack<TreeNode>();
        if(treeNode!=null)
        stack.push(treeNode);
    }

    @Override
    public boolean hasNext() {
        return !stack.isEmpty();
    }

    @Override
    public TreeNode next() {
        TreeNode treeNode = stack.pop();
        if(treeNode.getRight()!=null){
            stack.push(treeNode.getRight());
        }
        if(treeNode.getLeft()!=null){
            stack.push(treeNode.getLeft());
        }

        return treeNode;
    }

    @Override
    public void remove() {

    }
}


